package com.itjeffrey.autocode.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itjeffrey.autocode.bo.CreateModelParam;
import com.itjeffrey.autocode.bo.ProcessModel;
import com.itjeffrey.autocode.common.PageDomain;
import com.itjeffrey.autocode.common.PageResult;
import com.itjeffrey.autocode.common.Result;
import com.itjeffrey.autocode.enums.ExceptionEnum;
import com.itjeffrey.autocode.util.BeanOprUtils;
import com.itjeffrey.autocode.util.DateUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * Describe: 流程模型控制器
 */
@RestController
@RequestMapping(ControllerConstants.PROCESS_URL_PREFIX + "model")
public class ProcessModelController extends ControllerConstants {

    private static Logger log = LoggerFactory.getLogger(ProcessModelController.class);

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private ObjectMapper objectMapper;

    /**
     * Describe: 获取流程模型列表数据
     */
    @PostMapping("data")
    public ResponseEntity<Result> list(@RequestBody PageDomain pageDomain) {
        PageResult<ProcessModel> pageResult = new PageResult<>();
        BeanOprUtils.copyProperties(pageDomain, pageResult);
        ModelQuery modelQuery = repositoryService.createModelQuery();
        long count = modelQuery.list().size();
        if (StringUtils.hasText(pageDomain.getModelName())) {
            modelQuery.modelNameLike(pageDomain.getModelName());
        }
        List<Model> list = modelQuery
                .orderByCreateTime().desc()
                .orderByLastUpdateTime().desc()
                .listPage(pageDomain.start(), pageDomain.end());
        List<ProcessModel> data = new ArrayList<>();
        list.forEach(model -> {
            ProcessModel proModel = new ProcessModel();
            proModel.setId(model.getId());
            proModel.setKey(model.getKey());
            proModel.setName(model.getName());
            proModel.setVersion(model.getVersion());
            proModel.setCreateTime(DateUtil.dateToLocalDateTime(model.getCreateTime()));
            proModel.setUpdateTime(DateUtil.dateToLocalDateTime(model.getLastUpdateTime()));
            data.add(proModel);
        });
        pageResult.setTotals((int) count);
        pageResult.setData(data);
        return ResponseEntity.ok(Result.ok(pageResult));
    }

    /**
     * Describe: 创建流程图
     */
    @PostMapping("create")
    public ResponseEntity<Result> create(@RequestBody CreateModelParam param) throws Exception {
        Model model = repositoryService.newModel();
        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, param.getName());
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, param.getDescription());
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, param.getVersion());
        model.setName(param.getName());
        model.setKey(param.getKey());
        model.setMetaInfo(modelNode.toString());
        repositoryService.saveModel(model);
        createObjectNode(model.getId());
        return ResponseEntity.ok(Result.ok(model.getId()));
    }

    /**
     * Describe: 根据 Id 删除流程图
     */
    @PostMapping("delete/{id}")
    public ResponseEntity<Result> deleteById(@PathVariable("id") String modelId) {
        repositoryService.deleteModel(modelId);
        return ResponseEntity.ok(Result.ok());
    }

    /**
     * Describe: 模型发布流程
     */
    @ResponseBody
    @PostMapping("/publish/{id}")
    public ResponseEntity<Result> publish(@PathVariable("id") String modelId) {
        try {
            log.info("Process Model publish Start! modelId: {}===datetime:{}", modelId, DateUtil.getCurTime());
            Model modelData = repositoryService.getModel(modelId);
            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
            if (bytes == null) {
                return ResponseEntity.ok(Result.fail(ExceptionEnum.PROCESS_MODEL_EXISTS_NULL));
            }
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addBpmnModel(modelData.getKey() + ".bpmn20.xml", model)
                    .deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
            log.info("Process Model publish Success! modelId: {}===datetime:{}", modelId, DateUtil.getCurTime());
            return ResponseEntity.ok(Result.ok());
        } catch (Exception e) {
            log.error("Process Model publish Failed! modelId: {}===datetime:{}", modelId, DateUtil.getCurTime());
            return ResponseEntity.ok(Result.fail(ExceptionEnum.PROCESS_DEPLOY_FAIL));
        }
    }

    /**
     * Describe: 创建流程图节点信息
     */
    private void createObjectNode(String modelId) {
        log.info("ProcessModelController createObjectNode Start===modelId: {}===datetime: {}", modelId,
                DateUtil.getCurTime());
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        try {
            repositoryService.addModelEditorSource(modelId, editorNode.toString().getBytes("utf-8"));
        } catch (Exception e) {
            log.error("repositoryService addModelEditorSource Error, createObjectNode End===modelId: {}===datetime: " +
                    "{}", modelId, DateUtil.getCurTime(), e);
        }
        log.info("ProcessModelController createObjectNode End===modelId: {}===datetime: {}", modelId, DateUtil.getCurTime());
    }
}
